home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
options.lha
/
options
/
options.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-04-13
|
28KB
|
958 lines
// ****************************************************************************
// ^FILE: options.c - implement the functions defined in <options.h>
//
// ^HISTORY:
// 01/16/92 Brad Appleton <brad@ssd.csd.harris.com> Created
//
// 03/23/93 Brad Appleton <brad@ssd.csd.harris.com>
// - Added OptIstreamIter class
// ^^**************************************************************************
#include <stdlib.h>
#include <iostream.h>
#include <ctype.h>
#include <string.h>
#include "options.h"
extern "C" {
void exit(int);
}
static const char ident[] = "@(#)Options 1.00" ;
// I need a portable version of "tolower" that does NOT modify
// non-uppercase characters.
//
#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
// ************************************************************** OptIter
OptIter::~OptIter(void) {}
const char *
OptIter::operator()(void) {
const char * elt = curr();
(void) next();
return elt;
}
// ************************************************************** OptIterRwd
OptIterRwd::OptIterRwd(void) {}
OptIterRwd::~OptIterRwd(void) {}
// ************************************************************** OptArgvIter
OptArgvIter::~OptArgvIter(void) {}
const char *
OptArgvIter::curr(void) {
return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx];
}
void
OptArgvIter::next(void) {
if ((ndx != ac) && av[ndx]) ++ndx;
}
const char *
OptArgvIter::operator()(void) {
return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx++];
}
void
OptArgvIter::rewind(void) { ndx = 0; }
// ************************************************************** OptStrTokIter
static const char WHITESPACE[] = " \t\n\r\v\f" ;
const char * OptStrTokIter::default_delims = WHITESPACE ;
OptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters)
: len(unsigned(strlen(tokens))), str(tokens), seps(delimiters),
cur(NULL), tokstr(NULL)
{
if (seps == NULL) seps = default_delims;
tokstr = new char[len + 1];
(void) ::strcpy(tokstr, str);
cur = ::strtok(tokstr, seps);
}
OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; }
const char *
OptStrTokIter::curr(void) { return cur; }
void
OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); }
const char *
OptStrTokIter::operator()(void) {
const char * elt = cur;
if (cur) cur = ::strtok(NULL, seps);
return elt;
}
void
OptStrTokIter::rewind(void) {
(void) ::strcpy(tokstr, str);
cur = ::strtok(tokstr, seps);
}
// ************************************************************* OptIstreamIter
const unsigned OptIstreamIter::MAX_LINE_LEN = 1024 ;
// Constructor
OptIstreamIter::OptIstreamIter(istream & input) : is(input), tok_iter(NULL)
{
}
// Destructor
OptIstreamIter::~OptIstreamIter(void)
{
delete tok_iter;
}
const char *
OptIstreamIter::curr(void) {
const char * result = NULL;
if (tok_iter) result = tok_iter->curr();
if (result) return result;
fill();
return (! is) ? NULL : tok_iter->curr();
}
void
OptIstreamIter::next(void) {
const char * result = NULL;
if (tok_iter) result = tok_iter->operator()();
if (result) return;
fill();
if (! is) tok_iter->next();
}
const char *
OptIstreamIter::operator()(void)
{
const char * result = NULL;
if (tok_iter) result = tok_iter->operator()();
if (result) return result;
fill();
return (! is) ? NULL : tok_iter->operator()();
}
// What we do is this: for each line of text in the istream, we use
// a OptStrTokIter to iterate over each token on the line.
//
// If the first non-white character on a line is c_COMMENT, then we
// consider the line to be a comment and we ignore it.
//
void
OptIstreamIter::fill(void) {
char buf[OptIstreamIter::MAX_LINE_LEN];
do {
*buf = '\0';
is.getline(buf, sizeof(buf));
char * ptr = buf;
while (isspace(*ptr)) ++ptr;
if (*ptr && (*ptr != OptIstreamIter::c_COMMENT)) {
delete tok_iter;
tok_iter = new OptStrTokIter(ptr);
return;
}
} while (is);
}
// ******************************************************************* Options
// ---------------------------------------------------------------------------
// ^FUNCTION: verify - verify the syntax of each option-spec
//
// ^SYNOPSIS:
// static void verify(name, optv[])
//
// ^PARAMETERS:
// const char * name - name of this command
// const char * const optv[] - the vector of option-specs to inspect.
//
// ^DESCRIPTION:
// All we have to do is iterate through the option vector and make sure
// That each option-spec is of the proper format.
//
// ^REQUIREMENTS:
// - optv[] should be non-NULL and terminated by a NULL pointer.
//
// ^SIDE-EFFECTS:
// If an invalid option-spec is found, prints a message on cerr and
// exits with a status of 127.
//
// ^RETURN-VALUE:
// None.
//
// ^ALGORITHM:
// For each option-spec
// - ensure (length > 0)
// - verify the the second character is one of "|?:*+"
// end-for
// ^^-------------------------------------------------------------------------
static void
verify(const char * name, const char * const optv[])
{
int errors = 0;
if ((optv == NULL) || (! *optv)) return;
for (; *optv ; optv++) {
char *p;
if (! **optv) {
cerr << name << ": invalid option spec \"" << *optv << "\"." << endl ;
cerr << "\tmust be at least 1 character long." << endl ;
++errors;
}
if ((**optv) && ((*optv)[1]) &&
((p = strchr("|?:*+", (*optv)[1])) == NULL)) {
cerr << name << ": invalid option spec \"" << *optv << "\"." << endl ;
cerr << "\t2nd character must be in the set \"|?:*+\"." << endl ;
++errors;
}
}/*for*/
if (errors) exit(127);
}
Options::Options(const char * name, const char * const optv[])
: cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT),
nextchar(NULL), listopt(NULL)
{
const char * basename = ::strrchr(cmdname, '/');
if (basename) cmdname = basename + 1;
verify(name, optv);
}
Options::~Options(void) {}
// return values for a keyword matching function
enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ;
// Get the option-char of an option-spec.
inline static char
OptChar(const char * optspec) { return *optspec; }
// Get the long-option of an option-spec.
inline static const char *
LongOpt(const char * optspec) {
return ((optspec)[1] && (optspec)[2] &&
(! isspace((optspec)[2]))) ? ((optspec) + 2) : NULL ;
}
// Is the option-char null?
inline static int
isNullOpt(char optchar) {
return ((! optchar) || isspace(optchar) || (! isprint(optchar))) ;
}
// Does this option require an argument?
inline static int
isRequired(const char * optspec) {
return (((optspec)[1] == ':') || ((optspec)[1] == '+')) ;
}
// Does this option take an optional argument?
inline static int
isOptional(const char * optspec) {
return (((optspec)[1] == '?') || ((optspec)[1] == '*')) ;
}
// Does this option take no arguments?
inline static int
isNoArg(const char * optspec) {
return (((optspec)[1] == '|') || (! (optspec)[1])) ;
}
// Can this option take more than one argument?
inline static int
isList(const char * optspec) {
return (((optspec)[1] == '+') || ((optspec)[1] == '*')) ;
}
// Does this option take any arguments?
inline static int
isValTaken(const char * optspec) {
return (isRequired(optspec) || isOptional(optspec)) ;
}
// Check for explicit "end-of-options"
inline static int
isEndOpts(const char * token)
{
return ((token == NULL) || (! strcmp(token, "--"))) ;
}
// See if an argument is an option
inline static int
isOption(unsigned flags, const char * arg)
{
return (((*arg != '\0') || (arg[1] != '\0')) &&
((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))) ;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: kwdmatch - match a keyword
//
// ^SYNOPSIS:
// static kwdmatch_t kwdmatch(src, attempt, len)
//
// ^PARAMETERS:
// char * src -- the actual keyword to match
// char * attempt -- the possible keyword to compare against "src"
// int len -- number of character of "attempt" to consider
// (if 0 then we should use all of "attempt")
//
// ^DESCRIPTION:
// See if "attempt" matches some prefix of "src" (case insensitive).
//
// ^REQUIREMENTS:
// - attempt should be non-NULL and non-empty
//
// ^SIDE-EFFECTS:
// None.
//
// ^RETURN-VALUE:
// An enumeration value of type kwdmatch_t corresponding to whether
// We had an exact match, a partial match, or no match.
//
// ^ALGORITHM:
// Trivial
// ^^-------------------------------------------------------------------------
static kwdmatch_t
kwdmatch(const char * src, const char * attempt, int len =0)
{
int i;
if (src == attempt) return EXACT_MATCH ;
if ((src == NULL) || (attempt == NULL)) return NO_MATCH ;
if ((! *src) && (! *attempt)) return EXACT_MATCH ;
if ((! *src) || (! *attempt)) return NO_MATCH ;
for (i = 0 ; ((i < len) || (len == 0)) &&
(attempt[i]) && (attempt[i] != ' ') ; i++) {
if (TOLOWER(src[i]) != TOLOWER(attempt[i])) return NO_MATCH ;
}
return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: match_opt - match an option
//
// ^SYNOPSIS:
// static const char * match_opt(optv, opt)
//
// ^PARAMETERS:
// char * optv[] -- vector of option-specifications
// char opt -- the option-character to match
// int ignore_case -- should we ignore character-case?
//
// ^DESCRIPTION:
// See if "opt" is found in "optv"
//
// ^REQUIREMENTS:
// - optv should be non-NULL and terminated by a NULL pointer.
//
// ^SIDE-EFFECTS:
// None.
//
// ^RETURN-VALUE:
// NULL if no match is found,
// otherwise a pointer to the matching option-spec.
//
// ^ALGORITHM:
// foreach option-spec
// - see if "opt" is a match, if so return option-spec
// end-for
// ^^-------------------------------------------------------------------------
static const char *
match_opt(const char * const optv[], char opt, int ignore_case =0)
{
if ((optv == NULL) || (! *optv)) return NULL;
for (; *optv ; ++optv) {
char optchar = OptChar(*optv);
if (isNullOpt(optchar)) continue;
if (opt == optchar) {
return *optv;
} else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) {
return *optv;
}
}
return NULL; // not found
}
// ---------------------------------------------------------------------------
// ^FUNCTION: match_longopt - match a long-option
//
// ^SYNOPSIS:
// static const char * match_longopt(optv, opt, len, ambiguous)
//
// ^PARAMETERS:
// char * optv[] -- the vector of option-specs
// char * opt -- the long-option to match
// int len -- the number of character of "opt" to match
// int & ambiguous -- set by this routine before returning.
//
// ^DESCRIPTION:
// Try to match "opt" against some unique prefix of a long-option
// (case insensitive).
//
// ^REQUIREMENTS:
// - optvec should be non-NULL and terminated by a NULL pointer.
//
// ^SIDE-EFFECTS:
// - *ambiguous is set to '1' if "opt" matches >1 long-option
// (otherwise it is set to 0).
//
// ^RETURN-VALUE:
// NULL if no match is found,
// otherwise a pointer to the matching option-spec.
//
// ^ALGORITHM:
// ambiguous is FALSE
// foreach option-spec
// if we have an EXACT-MATCH, return the option-spec
// if we have a partial-match then
// if we already had a previous partial match then
// set ambiguous = TRUE and retrun NULL
// else
// remember this options spec and continue matching
// end-if
// end-if
// end-for
// if we had exactly 1 partial match return it, else return NULL
// ^^-------------------------------------------------------------------------
static const char *
match_longopt(const char * const optv[],
const char * opt,
int len,
int & ambiguous)
{
kwdmatch_t result;
const char * matched = NULL ;
ambiguous = 0;
if ((optv == NULL) || (! *optv)) return NULL;
for (; *optv ; ++optv) {
const char * longopt = LongOpt(*optv) ;
if (longopt == NULL) continue;
result = kwdmatch(longopt, opt, len);
if (result == EXACT_MATCH) {
return *optv;
} else if (result == PARTIAL_MATCH) {
if (matched) {
++ambiguous;
return NULL;
} else {
matched = *optv;
}
}
}/*for*/
return matched;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::parse_opt - parse an option
//
// ^SYNOPSIS:
// int Options::parse_opt(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// - nextchar must point to the prospective option character
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 'c' if the -c option was matched (optarg points to its argument)
// -1 if the option is invalid (optarg points to the bad option-character).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::parse_opt(OptIter & iter, const char * & optarg)
{
listopt = NULL; // reset the list pointer
if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS;
// Try to match a known option
const char * optspec = match_opt(optvec, *(nextchar++));
// Check for an unknown option
if (optspec == NULL) {
// See if this was a long-option in disguise
if (! (optctrls & NOGUESSING)) {
unsigned save_ctrls = optctrls;
const char * save_nextchar = nextchar;
nextchar -= 1;
optctrls |= (QUIET | NOGUESSING);
int optchar = parse_longopt(iter, optarg);
optctrls = save_ctrls;
if (optchar > 0) {
return optchar;
} else {
nextchar = save_nextchar;
}
}
if (! (optctrls & QUIET)) {
cerr << cmdname << ": unknown option -"
<< *(nextchar - 1) << "." << endl ;
}
optarg = (nextchar - 1); // record the bad option in optarg
return BADCHAR;
}
// If no argument is taken, then leave now
if (isNoArg(optspec)) {
optarg = NULL;
return OptChar(optspec);
}
// Check for argument in this arg
if (*nextchar) {
optarg = nextchar; // the argument is right here
nextchar = NULL; // we've exhausted this arg
if (isList(optspec)) listopt = optspec ; // save the list-spec
return OptChar(optspec);
}
// Check for argument in next arg
const char * nextarg = iter.curr();
if ((nextarg != NULL) &&
(isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
optarg = nextarg; // the argument is here
iter.next(); // end of arg - advance
if (isList(optspec)) listopt = optspec ; // save the list-spec
return OptChar(optspec);
}
// No argument given - if its required, thats an error
optarg = NULL;
if (isRequired(optspec) && !(optctrls & QUIET)) {
cerr << cmdname << ": argument required for -" << OptChar(optspec)
<< " option." << endl ;
}
return OptChar(optspec);
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::parse_longopt - parse a long-option
//
// ^SYNOPSIS:
// int Options::parse_longopt(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next long-option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// - nextchar must point to the prospective option character
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 'c' if the the long-option corresponding to the -c option was matched
// (optarg points to its argument)
// -2 if the option is invalid (optarg points to the bad long-option name).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::parse_longopt(OptIter & iter, const char * & optarg)
{
int len = 0, ambiguous = 0;
listopt = NULL ; // reset the list-spec
if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS;
// if a value is supplied in this argv element, get it now
const char * val = strpbrk(nextchar, ":=") ;
if (val) {
len = val - nextchar ;
++val ;
}
// Try to match a known long-option
const char * optspec = match_longopt(optvec, nextchar, len, ambiguous);
// Check for an unknown long-option
if (optspec == NULL) {
// See if this was a short-option in disguise
if ((! ambiguous) && (! (optctrls & NOGUESSING))) {
unsigned save_ctrls = optctrls;
const char * save_nextchar = nextchar;
optctrls |= (QUIET | NOGUESSING);
int optchar = parse_opt(iter, optarg);
optctrls = save_ctrls;
if (optchar > 0) {
return optchar;
} else {
nextchar = save_nextchar;
}
}
if (! (optctrls & QUIET)) {
cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown")
<< " option "
<< ((optctrls & LONG_ONLY) ? "-" : "--")
<< nextchar << "." << endl ;
}
optarg = nextchar; // record the bad option in optarg
nextchar = NULL; // we've exhausted this argument
return (ambiguous) ? AMBIGUOUS : BADKWD;
}
// If no argument is taken, then leave now
if (isNoArg(optspec)) {
if ((val) && ! (optctrls & QUIET)) {
cerr << cmdname << ": option "
<< ((optctrls & LONG_ONLY) ? "-" : "--")
<< LongOpt(optspec) << " does NOT take an argument." << endl ;
}
optarg = val; // record the unexpected argument
nextchar = NULL; // we've exhausted this argument
return OptChar(optspec);
}
// Check for argument in this arg
if (val) {
optarg = val; // the argument is right here
nextchar = NULL; // we exhausted the rest of this arg
if (isList(optspec)) listopt = optspec ; // save the list-spec
return OptChar(optspec);
}
// Check for argument in next arg
const char * nextarg = iter.curr(); // find the next argument to parse
if ((nextarg != NULL) &&
(isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
optarg = nextarg; // the argument is right here
iter.next(); // end of arg - advance
nextchar = NULL; // we exhausted the rest of this arg
if (isList(optspec)) listopt = optspec ; // save the list-spec
return OptChar(optspec);
}
// No argument given - if its required, thats an error
optarg = NULL;
if (isRequired(optspec) && !(optctrls & QUIET)) {
const char * longopt = LongOpt(optspec);
const char * spc = ::strchr(longopt, ' ');
int longopt_len;
if (spc) {
longopt_len = spc - longopt;
} else {
longopt_len = ::strlen(longopt);
}
cerr << cmdname << ": argument required for "
<< ((optctrls & LONG_ONLY) ? "-" : "--");
cerr.write(longopt, longopt_len) << " option." << endl ;
}
nextchar = NULL; // we exhausted the rest of this arg
return OptChar(optspec);
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::fmt_opt - format an option-spec for a usage message
//
// ^SYNOPSIS:
// unsigned Options::fmt_opt(optspec, buf)
//
// ^PARAMETERS:
// char * optspec -- the option-specification
// char * buf -- where to print the formatted option
//
// ^DESCRIPTION:
// Self-explanatory.
//
// ^REQUIREMENTS:
// - optspec must be a valid option-spec.
// - buf must be large enough to hold the result
//
// ^SIDE-EFFECTS:
// - writes to buf.
//
// ^RETURN-VALUE:
// Number of characters written to buf.
//
// ^ALGORITHM:
// Trivial.
// ^^-------------------------------------------------------------------------
unsigned
Options::fmt_opt(const char * optspec, char * buf) const
{
static char default_value[] = "<value>";
char optchar = OptChar(optspec);
const char * longopt = LongOpt(optspec);
char * p = buf ;
const char * value = NULL;
int longopt_len = 0;
int value_len = 0;
if (longopt) {
value = ::strchr(longopt, ' ');
longopt_len = (value) ? (value - longopt) : ::strlen(longopt);
} else {
value = ::strchr(optspec + 1, ' ');
}
while (value && (*value == ' ')) ++value;
if (value && *value) {
value_len = ::strlen(value);
} else {
value = default_value;
value_len = sizeof(default_value) - 1;
}
if ((optctrls & SHORT_ONLY) &&
((! isNullOpt(optchar)) || (optctrls & NOGUESSING))) {
longopt = NULL;
}
if ((optctrls & LONG_ONLY) && (longopt || (optctrls & NOGUESSING))) {
optchar = '\0';
}
if (isNullOpt(optchar) && (longopt == NULL)) {
*buf = '\0';
return 0;
}
*(p++) = '[';
// print the single character option
if (! isNullOpt(optchar)) {
*(p++) = '-';
*(p++) = optchar;
}
if ((! isNullOpt(optchar)) && (longopt)) *(p++) = '|';
// print the long option
if (longopt) {
*(p++) = '-';
if (! (optctrls & (LONG_ONLY | SHORT_ONLY))) {
*(p++) = '-';
}
strncpy(p, longopt, longopt_len);
p += longopt_len;
}
// print any argument the option takes
if (isValTaken(optspec)) {
*(p++) = ' ' ;
if (isOptional(optspec)) *(p++) = '[' ;
strcpy(p, value);
p += value_len;
if (isList(optspec)) {
strcpy(p, " ...");
p += 4;
}
if (isOptional(optspec)) *(p++) = ']' ;
}
*(p++) = ']';
*p = '\0';
return (unsigned) strlen(buf);
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::usage - print usage
//
// ^SYNOPSIS:
// void Options::usage(os, positionals)
//
// ^PARAMETERS:
// ostream & os -- where to print the usage
// char * positionals -- command-line syntax for any positional args
//
// ^DESCRIPTION:
// Print command-usage (using either option or long-option syntax) on os.
//
// ^REQUIREMENTS:
// os should correspond to an open output file.
//
// ^SIDE-EFFECTS:
// Prints on os
//
// ^RETURN-VALUE:
// None.
//
// ^ALGORITHM:
// Print usage on os, wrapping long lines where necessary.
// ^^-------------------------------------------------------------------------
void
Options::usage(ostream & os, const char * positionals) const
{
const char * const * optv = optvec;
unsigned cols = 79;
int first, nloop;
char buf[256] ;
if ((optv == NULL) || (! *optv)) return;
// print first portion "usage: progname"
os << "usage: " << cmdname ;
unsigned ll = strlen(cmdname) + 7;
// save the current length so we know how much space to skip for
// subsequent lines.
//
unsigned margin = ll + 1;
// print the options and the positional arguments
for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
unsigned len;
// figure out how wide this parameter is (for printing)
if (! *optv) {
len = strlen(positionals);
++nloop; // terminate this loop
} else {
len = fmt_opt(*optv, buf);
}
// Will this fit?
if ((ll + len + 1) > (cols - first)) {
os << '\n' ; // No - start a new line;
os.width(margin);
os << "" ;
ll = margin;
} else {
os << ' ' ; // Yes - just throw in a space
++ll;
}
ll += len;
os << ((nloop) ? positionals : buf) ;
}// for each ad
os << endl ;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::operator() - get options from the command-line
//
// ^SYNOPSIS:
// int Options::operator()(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// None.
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument is completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 0 if all options have been parsed.
// 'c' if the the option or long-option corresponding to the -c option was
// matched (optarg points to its argument).
// -1 if the option is invalid (optarg points to the bad option character).
// -2 if the option is invalid (optarg points to the bad long-option name).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::operator()(OptIter & iter, const char * & optarg)
{
explicit_end = 0;
// See if we have an option left over from before ...
if ((nextchar) && *nextchar) {
return parse_opt(iter, optarg);
}
// Check for end-of-options ...
const char * arg = iter.curr();
if (arg == NULL) {
listopt = NULL;
return ENDOPTS;
} else if (isEndOpts(arg)) {
iter.next(); // advance past end-of-options arg
listopt = NULL;
explicit_end = 1;
return ENDOPTS;
}
// Do we have a positional arg?
if (! listopt) {
if ((! *arg) || (! arg[1])) {
return ENDOPTS;
} else if ((*arg != '-') &&
((! (optctrls & PLUS)) || (*arg != '+'))) {
return ENDOPTS;
}
}
iter.next(); // pass the argument that arg already points to
// See if we have a long option ...
if (! (optctrls & SHORT_ONLY)) {
if ((*arg == '-') && (arg[1] == '-')) {
nextchar = arg + 2;
return parse_longopt(iter, optarg);
} else if ((optctrls & PLUS) && (*arg == '+')) {
nextchar = arg + 1;
return parse_longopt(iter, optarg);
}
}
if (*arg == '-') {
nextchar = arg + 1;
if (optctrls & LONG_ONLY) {
return parse_longopt(iter, optarg);
} else {
return parse_opt(iter, optarg);
}
}
// If we get here - it is because we have a list value
optarg = arg ; // record the list value
return OptChar(listopt) ;
}